﻿using System;
using System.ComponentModel;
using System.Xml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Attributes;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Enumerations;
using Microsoft.WowAddonStudio.FrameXml.Serialization.TypeConverters;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization 
{
    [Serializable]
    [DesignerCategory("code")]
    [XmlType(Namespace = "http://www.blizzard.com/wow/ui/")]
	[TypeConverter(typeof(EmptyExpandableObjectConverter))]
    public class Backdrop : SerializationObject, ICloneable, ICopiable<Backdrop>, IEquatable<Backdrop>
    {
        /// <summary>
        /// Gets or sets the background file used for the backdrop.
        /// </summary>
        [XmlAttribute("bgFile")]
		[File(FileType.Image)]
        [NotifyParentProperty(true)]
		[Description("Which texture file to use as frame background (.blp or .tga format).")]
        public string BackgroundFile { get; set; }

        /// <summary>
        /// Gets or sets the edge file used for the backdrop.
        /// </summary>
        [XmlAttribute("edgeFile")]
        [File(FileType.Image)]
        [NotifyParentProperty(true)]
		[Description("Which texture file to use as frame edge blp or .tga format).")]
        public string EdgeFile { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the background file should be tiled.
        /// </summary>
        [XmlAttribute("tile")]
        [DefaultValue(false)]
        [NotifyParentProperty(true)]
		[Description("Determines if the background texture will be stretched all over the Backdrop area, or repeated in a form of tiles.")]
        public bool Tile { get; set; }

        /// <summary>
        /// Gets or sets the background insets.
        /// </summary>
        /// <remarks>
        /// Only <c>AbsInset</c>s are supported.
        /// </remarks>
        [XmlArray]
        [XmlArrayItem("AbsInset")]
        [Browsable(false)]
		[Description("4 x Number - Controls how far into the frame the background will be drawn (use higher values the thicker the edges are).")]
        public Inset[] BackgroundInsets { get; set; }

        /// <summary>
        /// Gets or sets a single background inset. This property is not serialized and wraps <see cref="BackgroundInsets"/>.
        /// </summary>
        [XmlIgnore]
        [NotifyParentProperty(true)]
		[Description("4 x Number - Controls how far into the frame the background will be drawn (use higher values the thicker the edges are).")]
        public Inset BackgroundInset
        {
            get
            {
                if (this.BackgroundInsets != null && this.BackgroundInsets.Length > 0)
                    return this.BackgroundInsets[0];

                return null;
            }
            set { this.BackgroundInsets = new[] {value}; }
        }

        /// <summary>
        /// Gets or sets the tile sizes.
        /// </summary>
        /// <remarks>
        /// Only <c>AbsValue</c>s are supported.
        /// </remarks>
        [XmlArray("TileSize")]
        [XmlArrayItem("AbsValue")]
        [Browsable(false)]
		[Description("Control how large each copy of the bgFile becomes on-screen.")]
        public AbsValue[] TileSizes { get; set; }

        /// <summary>
        /// Gets or sets a single tile size. This property is not serialized and wraps <see cref="EdgeSizes"/>.
        /// </summary>
        [XmlIgnore]
        [NotifyParentProperty(true)]
		[Description("Control how large each copy of the bgFile becomes on-screen.")]
        public int? TileSize
        {
            get
            {
                if (this.TileSizes != null && this.TileSizes.Length > 0)
                    return this.TileSizes[0].Value;

                return null;
            }
            set
            {
                this.TileSizes = value.HasValue ? new[] { new AbsValue { Value = value.Value } } : null;
            }
        }        
        /// <summary>
        /// Gets or sets the edge sizes.
        /// </summary>
        /// <remarks>
        /// Only <c>AbsValue</c>s are supported.
        /// </remarks>
        [XmlArray("EdgeSize")]
        [XmlArrayItem("AbsValue")]
        [Browsable(false)]
		[Description("Control how large each copy of the edgeFile becomes on-screen (i.e. border thickness and corner size).")]
        public AbsValue[] EdgeSizes { get; set; }

        /// <summary>
        /// Gets or sets a single tile size. This property is not serialized and wraps <see cref="EdgeSizes"/>.
        /// </summary>
        [XmlIgnore]
        [NotifyParentProperty(true)]
		[Description("Control how large each copy of the edgeFile becomes on-screen (i.e. border thickness and corner size).")]
        public int? EdgeSize
        {
            get
            {
                if (this.EdgeSizes != null && this.EdgeSizes.Length > 0)
                    return this.EdgeSizes[0].Value;

                return null;
            }
            set
            {
                this.EdgeSizes = value.HasValue ? new[] { new AbsValue { Value = value.Value } } : null;
            }
        }

        /// <summary>
        /// Gets or sets the color of the backdrop.
        /// </summary>
        [XmlElement]
		[EditorBrowsable]
		[NotifyParentProperty(true)]
		[Description("The color of backdrop.")]
        [Color]
        public Color Color { get; set; }

        /// <summary>
        /// Gets or sets the border color of the backdrop.
        /// </summary>
        [XmlElement]
		[EditorBrowsable]
		[NotifyParentProperty(true)]
		[Description("The border color of backdrop.")]
        [Color]
        public Color BorderColor { get; set; }

        /// <summary>
        /// Gets a value indicating whether a serialization object is complete and can be rendered.
        /// </summary>
        /// <value></value>
        public override bool IsComplete
        {
            get
            {
                // TODO
                return true;
            }
        }

        /// <summary>
        /// Creates a new object that is a copy of the instance.
        /// </summary>
        /// <returns>The copied instance.</returns>
        /// <remarks>Performs a deep copy.</remarks>
        public Backdrop Copy()
        {
            return new Backdrop
                       {
                           BackgroundInset = BackgroundInset != null ? BackgroundInset.Copy() : null,
                           BorderColor = BorderColor != null ? BorderColor.Copy() : null,
                           Color = Color != null ? Color.Copy() : null,
                           BackgroundFile = BackgroundFile,
                           EdgeFile = EdgeFile,
                           EdgeSize = EdgeSize,
                           TileSize = TileSize,
                           Tile = Tile
                       };
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public object Clone()
        {
            return this.Copy();
        }

        /// <summary>
        /// Equalses the specified obj.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public bool Equals(Backdrop obj)
        {
            if (ReferenceEquals(null, obj))
                return false;
            if (ReferenceEquals(this, obj))
                return true;
            return Equals(obj.BackgroundFile, BackgroundFile) && Equals(obj.EdgeFile, EdgeFile) && obj.Tile.Equals(Tile) && Equals(obj.BackgroundInsets, BackgroundInsets) && Equals(obj.TileSizes, TileSizes) && Equals(obj.EdgeSizes, EdgeSizes) && Equals(obj.Color, Color) && Equals(obj.BorderColor, BorderColor);
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
                return false;
            if (ReferenceEquals(this, obj))
                return true;
            if (obj.GetType() != typeof (Backdrop))
                return false;
            return Equals((Backdrop) obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            unchecked
            {
                int result = (BackgroundFile != null ? BackgroundFile.GetHashCode() : 0);
                result = (result*397) ^ (EdgeFile != null ? EdgeFile.GetHashCode() : 0);
                result = (result*397) ^ Tile.GetHashCode();
                result = (result*397) ^ (BackgroundInsets != null ? BackgroundInsets.GetHashCode() : 0);
                result = (result*397) ^ (TileSizes != null ? TileSizes.GetHashCode() : 0);
                result = (result*397) ^ (EdgeSizes != null ? EdgeSizes.GetHashCode() : 0);
                result = (result*397) ^ (Color != null ? Color.GetHashCode() : 0);
                result = (result*397) ^ (BorderColor != null ? BorderColor.GetHashCode() : 0);
                return result;
            }
        }
    }
}